#ifndef _VULKAN_SCENE_H_
#define _VULKAN_SCENE_H_

#include "std.hpp"

namespace Vulkan {
	class Scene
	{
	public:
		Camera camera;
		inline void init() {
			texture.object = Loader::loadTexture(getResourcePath("model/panda/panda_rgba8.ktx"),VK_FORMAT_R8G8B8A8_UNORM);
			texture.skybox = Loader::loadTexture(getResourcePath("model/cubemap_yokohama.ktx"), VK_FORMAT_BC3_UNORM_BLOCK);
			model.object = Loader::loadModel(getResourcePath("model/panda/panda.DAE"));
			model.skybox = Loader::loadModel(getResourcePath("model/cube.obj"));
			camera.init();
			camera.setPerspective(60.0f, (float)context.extend.width / (float)context.extend.height, 0.1f, 1000.0f);
			camera.update();

			setupPipeline();
			setupDescriptorSet();

			for (auto& mesh : model.object.meshes)
			{
				mesh.pipeline = pipeline.object;
				mesh.layout = pipeline.layout;
				mesh.set = descriptor.skyboxSet;
			}

			for (auto& mesh : model.skybox.meshes)
			{
				mesh.pipeline = pipeline.skybox;
				mesh.layout = pipeline.layout;
				mesh.set = descriptor.skyboxSet;
			}

			semaphores.present = createSemaphore();
			semaphores.render = createSemaphore();

			VkViewport viewport = {
				0,0,
				(float)context.extend.width,(float)context.extend.height,
				0.0f,1.0f
			};
			VkRect2D scissor = {
				{0,0},
				context.extend
			};

			VkClearValue clearValues[2];
			clearValues[0].color = { 0.2f, 0.0f, 0.0f, 1.0f };
			clearValues[1].depthStencil = { 1.0f, 0 };
			VkRenderPassBeginInfo renderPassBeginInfo = {};
			renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
			renderPassBeginInfo.renderPass = context.renderPass;
			renderPassBeginInfo.renderArea = scissor;
			renderPassBeginInfo.clearValueCount = 2;
			renderPassBeginInfo.pClearValues = clearValues;

			drawCommandBuffer = createCommandBuffer(3);
			for (size_t i = 0; i < drawCommandBuffer.size(); i++)
			{
				auto cur = drawCommandBuffer[i];
				VkCommandBufferBeginInfo beginInfo = {};
				beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
				beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
				vkBeginCommandBuffer(cur, &beginInfo);

				renderPassBeginInfo.framebuffer = context.swapchainFrames[i];
				vkCmdBeginRenderPass(cur, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
				vkCmdSetViewport(cur, 0, 1, &viewport);
				vkCmdSetScissor(cur, 0, 1, &scissor);

				VkDeviceSize offsets[1] = { 0 };
				// skybox
				{
					renderModel(model.skybox, cur);
				}
				//object
				{
					renderModel(model.object,cur);
				}
				
				vkCmdEndRenderPass(cur);
				vkEndCommandBuffer(cur);
			}
			isInit = true;
		}

		inline void draw() {
			if (!isInit)return;
			uint32_t currentBuffer = 0;
			vkAcquireNextImageKHR(context.device, context.swapchain, UINT64_MAX, semaphores.present, VK_NULL_HANDLE, &currentBuffer);

			VkPipelineStageFlags mask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
			VkSubmitInfo submitInfo = {};
			submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
			submitInfo.commandBufferCount = 1;
			submitInfo.pCommandBuffers = &drawCommandBuffer[currentBuffer];
			submitInfo.waitSemaphoreCount = 1;
			submitInfo.pWaitSemaphores = &semaphores.present;
			submitInfo.signalSemaphoreCount = 1;
			submitInfo.pSignalSemaphores = &semaphores.render;
			submitInfo.pWaitDstStageMask = &mask;
			vkQueueSubmit(context.graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);

			VkPresentInfoKHR presentInfo = {};
			presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
			presentInfo.swapchainCount = 1;
			presentInfo.pSwapchains = &context.swapchain;
			presentInfo.waitSemaphoreCount = 1;
			presentInfo.pWaitSemaphores = &semaphores.render;
			presentInfo.pImageIndices = &currentBuffer;
			vkQueuePresentKHR(context.graphicsQueue, &presentInfo);
		}
	private:
		struct
		{
			Model object;
			Model skybox;
		}model;
		struct
		{
			Texture object;
			Texture skybox;
		}texture;

		struct
		{
			VkPipeline object = VK_NULL_HANDLE;
			VkPipeline skybox = VK_NULL_HANDLE;
			VkPipelineLayout layout = VK_NULL_HANDLE;
		}pipeline;

		struct
		{
			VkDescriptorSetLayout layout = VK_NULL_HANDLE;
			VkDescriptorPool pool = VK_NULL_HANDLE;
			VkDescriptorSet objectSet = VK_NULL_HANDLE;
			VkDescriptorSet skyboxSet = VK_NULL_HANDLE;
		}descriptor;

		std::vector<VkCommandBuffer> drawCommandBuffer;
		
		struct
		{
			VkSemaphore present = VK_NULL_HANDLE;
			VkSemaphore render = VK_NULL_HANDLE;
		}semaphores;
		bool isInit = false;

		void renderModel(const Model& curModel,const VkCommandBuffer& cmd) {
			VkDeviceSize offsets[1] = { 0 };
			vkCmdBindVertexBuffers(cmd, 0, 1, &curModel.vertex.buffer, offsets);
			vkCmdBindIndexBuffer(cmd, curModel.index.buffer, 0, VK_INDEX_TYPE_UINT32);
			for (auto& mesh : curModel.meshes)
			{
				camera.mvp.model *= mesh.transform;
				camera.update();
				vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, mesh.pipeline);
				vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, mesh.layout, 0, 1, &mesh.set, 0, nullptr);
				vkCmdDrawIndexed(cmd, mesh.indexCount, 1, mesh.indexOffset, 0, 0);
			}
		}

		void setupPipeline() {
			std::vector<VkDescriptorPoolSize> poolSizes =
			{
				{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2 },
				{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2 },
			};
			descriptor.pool = createDescriptorPool(poolSizes, 2);
			descriptor.layout = Pipeline::createBaseSetLayout();
			pipeline.layout = createPipelineLayout(1, &descriptor.layout);

			std::vector<VkPipelineShaderStageCreateInfo> shaderStages = {
				Pipeline::shaderStageCreateInfo(getResourcePath("shader/reflect.vert.spv"),VK_SHADER_STAGE_VERTEX_BIT),
				Pipeline::shaderStageCreateInfo(getResourcePath("shader/reflect.frag.spv"),VK_SHADER_STAGE_FRAGMENT_BIT)
			};
			auto vertexBindings = Vertex::bindings();
			auto vertexAttributes = Vertex::attributes();
			VkPipelineColorBlendAttachmentState blendAttachment = {};
			blendAttachment.blendEnable = VK_FALSE;
			blendAttachment.colorWriteMask = 0xf;//must need to write
			auto info = Pipeline::pipelineCreateInfo(context.renderPass, pipeline.layout);
			info.stageCount = (uint32_t)shaderStages.size();
			info.pStages = shaderStages.data();
			auto vertexInputState = Pipeline::pipelineVertexInputStateCreateInfo(vertexBindings, vertexAttributes);
			info.pVertexInputState = &vertexInputState;
			auto inputAssemblyState = Pipeline::pipelineInputAssemblyStateCreateInfo();
			info.pInputAssemblyState = &inputAssemblyState;
			auto rasterizationState = Pipeline::pipelineRasterizationStateCreateInfo();
			info.pRasterizationState = &rasterizationState;
			auto colorBlendState = Pipeline::pipelineColorBlendStateCreateInfo(1, &blendAttachment);
			info.pColorBlendState = &colorBlendState;
			auto depthStencilState = Pipeline::pipelineDepthStencilStateCreateInfo();
			info.pDepthStencilState = &depthStencilState;
			auto multisampleState = Pipeline::pipelineMultisampleStateCreateInfo();
			info.pMultisampleState = &multisampleState;
			auto viewportState = Pipeline::pipelineViewportStateCreateInfo();
			info.pViewportState = &viewportState;
			VkDynamicState dynamic[] = { VK_DYNAMIC_STATE_VIEWPORT,VK_DYNAMIC_STATE_SCISSOR };
			auto dynamicState = Pipeline::pipelineDynamicStateCreateInfo(2,dynamic);
			info.pDynamicState = &dynamicState;
			vkCreateGraphicsPipelines(context.device, VK_NULL_HANDLE, 1, &info, nullptr, &pipeline.object);

			std::vector<VkPipelineShaderStageCreateInfo> skyboxStages = {
				Pipeline::shaderStageCreateInfo(getResourcePath("shader/skybox.vert.spv"),VK_SHADER_STAGE_VERTEX_BIT),
				Pipeline::shaderStageCreateInfo(getResourcePath("shader/skybox.frag.spv"),VK_SHADER_STAGE_FRAGMENT_BIT)
			};

			info.pStages = skyboxStages.data();
			depthStencilState = Pipeline::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_FALSE);
			info.pDepthStencilState = &depthStencilState;
			vkCreateGraphicsPipelines(context.device, VK_NULL_HANDLE, 1, &info, nullptr, &pipeline.skybox);
		}

		void setupDescriptorSet() {
			descriptor.objectSet = createDescriptorSet(descriptor.pool, &descriptor.layout);
			descriptor.skyboxSet = createDescriptorSet(descriptor.pool, &descriptor.layout);

			VkWriteDescriptorSet writes[2] = {};
			writes[0].sType = writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
			writes[0].descriptorCount = writes[1].descriptorCount = 1;
			writes[0].dstSet = writes[1].dstSet = descriptor.objectSet;
			writes[0].dstBinding = 0;
			writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
			writes[0].pBufferInfo = &camera.descriptor;

			writes[1].dstBinding = 1;
			writes[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
			writes[1].pImageInfo = &texture.object.descriptor;

			vkUpdateDescriptorSets(context.device, 2, writes, 0, NULL);

			writes[0].dstSet = writes[1].dstSet = descriptor.skyboxSet;
			writes[1].pImageInfo = &texture.skybox.descriptor;
			vkUpdateDescriptorSets(context.device, 2, writes, 0, NULL);
		}
	};
}

#endif
